package net.lgy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import net.lgy.component.StoreEngine;
import net.lgy.config.AccountConfig;
import net.lgy.config.MinioConfig;
import net.lgy.config.RabbitMQConfig;
import net.lgy.controller.req.AccountLoginReq;
import net.lgy.controller.req.AccountRegisterReq;
import net.lgy.controller.req.FolderCreateReq;
import net.lgy.dto.AccountDTO;
import net.lgy.dto.StorageDTO;
import net.lgy.enums.AccountRoleEnum;
import net.lgy.enums.BizCodeEnum;
import net.lgy.exception.BizException;
import net.lgy.mapper.AccountFileMapper;
import net.lgy.mapper.AccountMapper;
import net.lgy.mapper.StorageMapper;
import net.lgy.model.AccountDO;
import net.lgy.model.AccountFileDO;
import net.lgy.model.StorageDO;
import net.lgy.service.AccountFileService;
import net.lgy.service.AccountService;
import net.lgy.util.CommonUtil;
import net.lgy.util.SpringBeanUtil;
import org.apache.commons.codec.digest.Md5Crypt;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 账户服务实现类
 * 处理账户相关核心业务，包括注册、登录、头像上传、账户信息查询等功能
 * 依赖账户、存储、文件相关的数据访问层和服务层，确保业务流程完整性
 */
@Service
@Slf4j
public class AccountServiceImpl implements AccountService {

    // 账户数据访问层：操作账户表（如查询、插入、更新账户信息）
    @Autowired
    private AccountMapper accountMapper;

    // 文件存储引擎：封装文件存储逻辑（如MinIO上传），解耦存储实现
    @Autowired
    private StoreEngine fileStoreEngine;

    // MinIO配置类：存储MinIO服务地址、桶名称等配置信息
    @Autowired
    private MinioConfig minioConfig;

    // 账户文件服务：处理文件夹创建等文件相关业务
    @Autowired
    private AccountFileService accountFileService;

    // 存储数据访问层：操作存储空间表（查询/更新用户存储空间使用情况）
    @Autowired
    private StorageMapper storageMapper;

    // 账户文件数据访问层：操作账户文件表（查询根目录等文件信息）
    @Autowired
    private AccountFileMapper accountFileMapper;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 账户注册业务
     * 核心流程：1. 校验手机号唯一性 2. 密码加密 3. 插入账户数据 4. 初始化存储空间 5. 创建用户根目录
     * 事务保证：注册过程中所有操作原子性，任一环节失败则回滚
     *
     * @param req 注册请求参数（包含手机号、密码等）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(AccountRegisterReq req) {
        // 1. 校验手机号是否已被注册
        List<AccountDO> accountDOList = accountMapper.selectList(
                new QueryWrapper<AccountDO>().eq("phone", req.getPhone())
        );
        if (!accountDOList.isEmpty()) {
            log.warn("手机号已重复注册，手机号：{}", req.getPhone());
            throw new BizException(BizCodeEnum.ACCOUNT_REPEAT);
        }

        // 2. 构建账户DO对象，准备插入数据库
        AccountDO accountDO = SpringBeanUtil.copyProperties(req, AccountDO.class);
        // 查询当前手机号对应的账户（此处逻辑需结合实际业务，若为新注册应先插入再查询，原代码逻辑需确认）
        AccountDTO byPhone = findByPhone(req.getPhone());
        Long accountNo = byPhone.getAccountNo();

        // 3. 密码加密：使用Md5Crypt加盐加密，盐值为账户secret字段
        String cryptPwd = Md5Crypt.md5Crypt(req.getPwd().getBytes(), accountDO.getSecret());
        accountDO.setPwd(cryptPwd);
        // 设置账户角色为普通用户
        accountDO.setRole(AccountRoleEnum.COMMON.name());
        // 插入账户数据（原代码注释，需根据实际业务确认是否启用）
        // accountMapper.insert(accountDO);

        // 4. 初始化用户存储空间：设置默认总容量，初始已用容量为0
        StorageDO storageDO = new StorageDO();
        storageDO.setAccountNo(accountNo);
        storageDO.setUsedSize(0L);
        storageDO.setTotalSize(AccountConfig.DEFAULT_STORAGE_SIZE);
        int rows = storageMapper.insert(storageDO);
        log.info("用户存储空间初始化完成，账户编号：{}，默认容量：{}", accountNo, AccountConfig.DEFAULT_STORAGE_SIZE);
        if (rows == 0){
            throw new BizException(BizCodeEnum.STORE_NOT_EXISTS);
        }
        // 5. 初始化用户根目录：创建默认根文件夹（如"我的文件"）
        FolderCreateReq createRootFolderReq = FolderCreateReq.builder()
                .accountNo(accountNo)
                .parentId(AccountConfig.ROOT_PARENT_ID) // 根目录父ID（配置中定义的顶级ID）
                .folderName(AccountConfig.ROOT_FOLDER_NAME) // 根目录名称（如"我的文件"）
                .type(1)
                .build();
        accountFileService.createFolder(createRootFolderReq);
        log.info("用户根目录初始化完成，账户编号：{}，根目录名称：{}", accountNo, AccountConfig.ROOT_FOLDER_NAME);
    }

    /**
     * 上传用户头像
     * 核心逻辑：生成唯一文件名，通过存储引擎上传到MinIO指定桶，返回头像访问URL
     *
     * @param file 头像文件（MultipartFile类型，前端上传的文件流）
     * @return 头像访问URL（MinIO服务地址+桶名称+文件名）
     */
    @Override
    public String uploadAvatar(MultipartFile file) {
        // 生成唯一文件名：避免文件名重复，结合工具类生成带路径的唯一名称
        String filename = CommonUtil.getFilePath(file.getOriginalFilename());
        // 上传文件到MinIO的头像专用桶
        fileStoreEngine.upload(minioConfig.getImgBucketName(), filename, file);
        log.info("用户头像上传完成，文件名：{}，存储桶：{}", filename, minioConfig.getImgBucketName());

        // 拼接头像访问URL并返回
        return minioConfig.getEndpoint() + "/" + minioConfig.getImgBucketName() + "/" + filename;
    }

    /**
     * 账户登录业务
     * 核心流程：1. 根据手机号查询账户 2. 密码加密校验 3. 校验通过返回账户DTO
     *
     * @param req 登录请求参数（包含手机号、密码）
     * @return 登录成功的账户DTO（包含账户基本信息）
     */
    @Override
    public AccountDTO login(AccountLoginReq req) {
        // 1. 根据手机号查询账户信息
        AccountDTO accountDTO = findByPhone(req.getPhone());
        if (accountDTO == null) {
            log.warn("登录失败：手机号未注册，手机号：{}", req.getPhone());
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER); // 需确认是否有该枚举值，原代码用PWD_ERROR，此处逻辑可优化
        }
        if (req.getPwd() == null){
            req.setPwd(req.getPassword());
        }
        // 2. 密码校验：将请求密码按相同规则加密后与数据库存储的密码对比
        String md5Crypt = Md5Crypt.md5Crypt(req.getPwd().getBytes(), accountDTO.getSecret());
        if (!md5Crypt.equalsIgnoreCase(accountDTO.getPwd())) {
            log.warn("登录失败：密码错误，手机号：{}", req.getPhone());
            throw new BizException(BizCodeEnum.ACCOUNT_PWD_ERROR);
        }
        Map<String,Object> sendLogin = new HashMap<>();
        sendLogin.put("phone",req.getPhone());
        sendLogin.put("pwd",req.getPwd());
        sendLogin.put("captcha","kaelis");

        rabbitTemplate.convertAndSend(rabbitMQConfig.getLoginEventExchange(), rabbitMQConfig.getLoginEventRoutingKey(), sendLogin);


        log.info("账户登录成功，手机号：{}，账户编号：{}", req.getPhone(), accountDTO.getAccountNo());
        return accountDTO;
    }

    /**
     * 查询账户详细信息
     * 核心逻辑：1. 查询账户基本信息 2. 查询存储空间信息 3. 查询根目录信息 4. 组装DTO返回
     *
     * @param accountNo 账户编号（唯一标识）
     * @param phone     手机号（辅助查询，可根据业务调整）
     * @return 账户详细DTO（包含基本信息、存储空间、根目录信息）
     */
    @Override
    public AccountDTO queryDetail(Long accountNo, String phone) {
        // 1. 查询账户基本信息（通过账户编号）
        AccountDTO accountDTO = findByAccountNo(accountNo);
        if (accountDTO == null) {
            log.warn("查询账户详情失败：账户不存在，账户编号：{}", accountNo);
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER);
        }

        // 2. 查询账户存储空间信息（已用容量、总容量）
        StorageDO storageDO = storageMapper.selectOne(
                new QueryWrapper<StorageDO>().eq("account_no", accountNo)
        );
        accountDTO.setStorageDTO(SpringBeanUtil.copyProperties(storageDO, StorageDTO.class));

        // 3. 查询账户根目录信息（根文件夹ID和名称）
        AccountFileDO accountFileDO = accountFileMapper.selectOne(
                new QueryWrapper<AccountFileDO>()
                        .eq("account_no", accountNo)
                        .eq("parent_id", AccountConfig.ROOT_PARENT_ID) // 根目录父ID（配置定义）
        );
        accountDTO.setRootFileId(accountFileDO.getId());
        accountDTO.setRootFileName(accountFileDO.getFileName());

        log.info("查询账户详情成功，账户编号：{}", accountNo);
        return accountDTO;
    }

    /**
     * 根据手机号查询账户信息
     * 核心逻辑：调用Mapper层方法查询账户DO，转换为DTO返回
     *
     * @param phone 手机号（唯一标识之一）
     * @return 账户DTO（包含账户基本信息）
     */
    @Override
    public AccountDTO findByPhone(String phone) {
        // 从数据库查询手机号对应的账户DO
        AccountDO accountDO = accountMapper.findByPhone(phone);
        if (accountDO == null) {
            log.warn("根据手机号查询账户失败：账户不存在，手机号：{}", phone);
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER);
        }

        // 转换DO为DTO并返回
        return SpringBeanUtil.copyProperties(accountDO, AccountDTO.class);
    }

    /**
     * 插入/更新AI网盘Token
     * 核心逻辑：根据手机号更新账户表中的AI网盘Token字段
     *
     * @param phone 手机号（关联账户）
     * @param token AI网盘Token（需存储的令牌）
     * @return 受影响的行数（1表示成功，0表示未找到对应账户）
     */
    @Override
    public int insertAiPanToken(String phone, String token) {
        // 构建更新条件：手机号匹配，更新token_ai_pan字段
        UpdateWrapper<AccountDO> updateWrapper = new UpdateWrapper<AccountDO>()
                .eq("phone", phone)
                .set("token_ai_pan", token);

        // 执行更新操作
        int updateRows = accountMapper.update(null, updateWrapper);
        log.info("更新AI网盘Token完成，手机号：{}，受影响行数：{}", phone, updateRows);

        return updateRows;
    }

    /**
     * 根据账户编号查询账户信息
     * 核心逻辑：调用Mapper层方法查询账户DO，转换为DTO返回
     *
     * @param accountNo 账户编号（唯一标识）
     * @return 账户DTO（包含账户基本信息）
     */
    @Override
    public AccountDTO findByAccountNo(Long accountNo) {
        // 从数据库查询账户编号对应的账户DO
        AccountDO accountDO = accountMapper.findByAccountNo(accountNo);
        if (accountDO == null) {
            log.warn("根据账户编号查询账户失败：账户不存在，账户编号：{}", accountNo);
            throw new BizException(BizCodeEnum.ACCOUNT_UNREGISTER);
        }

        // 转换DO为DTO并返回
        return SpringBeanUtil.copyProperties(accountDO, AccountDTO.class);
    }
}